// ZenCoder - A Video Encoding software for your Creative Zen
// Copyright 2008 Andrea Cimitan <andrea.cimitan@gmail.com>

using System;
using System.Threading;
using System.Text.RegularExpressions;
using Gtk;
using GLib;
using Gdk;

class ZenCoder
{
	private static Gtk.TargetEntry [ ] targetTable =
	               new TargetEntry [ ] { new TargetEntry("text/uri-list",0,0),};
	private static Gdk.Pixbuf icon;
	private static Gtk.ProgressBar pBar;
	private static Gtk.ListStore listStore;
	private static Gtk.SpinButton audioSpinButton;
	private static Gtk.Label audioBitrateLabel;
	private static Gtk.CheckButton audioCheck;
	private static Gtk.Button skipCurrentButton;
	private static int videoBitrate;
	private static int audioBitrate;
	private static Gtk.Label statusLabel;
	private static object lockI = new object ();
	private static System.Diagnostics.Process proc;
	private static bool pulsing;

	public static void Main(string[  ] args)
	{
		Application.Init();

		// widgets
		Gtk.Window w = new Gtk.Window("ZenCoder");
		HBox mainHBox = new HBox();
		HBox dropHBox = new HBox();
		HBox spinVideoHBox = new HBox();
		HBox spinAudioHBox = new HBox();
		HBox statusButtonHBox = new HBox();
		VBox mainVBox = new VBox();
		VBox leftVBox = new VBox();
		VBox rightVBox = new VBox();
		VBox prefVBox = new VBox();
		VBox statusVBox = new VBox();
		MenuBar mb = new MenuBar ();
		//Gtk.Frame dndFrame = new Gtk.Frame();
		Gtk.Frame prefFrame = new Gtk.Frame("Preferences");
		Gtk.Frame statusFrame = new Gtk.Frame("Status");
		icon = new Pixbuf(null, "zen.png");
		Gtk.EventBox image = new Gtk.EventBox();
		Gtk.Label dropLabel = new Gtk.Label("Drop videos here\nto convert");
		Gtk.Label videoBitrateLabel = new Gtk.Label("Video bitrate:");
		audioBitrateLabel = new Gtk.Label("Audio bitrate:");
		audioCheck = new Gtk.CheckButton();
		statusLabel = new Gtk.Label("Idle");
		SpinButton videoSpinButton = new SpinButton(0f, 2000f, 1f);
		audioSpinButton = new SpinButton(0f, 320f, 1f);
		pBar = new ProgressBar();
		skipCurrentButton = new Gtk.Button();

		Gtk.ScrolledWindow scrollWin = new Gtk.ScrolledWindow();
		Gtk.TreeView tree = new TreeView();
		listStore = new ListStore(typeof (String));

		// set properties
		w.Icon = icon;

		if (w.Screen.RgbaColormap != null)
			Gtk.Widget.DefaultColormap = w.Screen.RgbaColormap;

		mainHBox.BorderWidth = 6;
		mainHBox.Spacing = 6;
		dropHBox.BorderWidth = 6;
		dropHBox.Spacing = 6;
		spinVideoHBox.BorderWidth = 6;
		spinVideoHBox.Spacing = 6;
		spinAudioHBox.BorderWidth = 6;
		spinAudioHBox.Spacing = 6;
		statusButtonHBox.BorderWidth = 0;
		statusButtonHBox.Spacing = 6;
		leftVBox.BorderWidth = 6;
		leftVBox.Spacing = 6;
		rightVBox.BorderWidth = 6;
		rightVBox.Spacing = 6;
		prefVBox.BorderWidth = 6;
		prefVBox.Spacing = 6;
		statusVBox.BorderWidth = 6;
		statusVBox.Spacing = 6;
		statusLabel.Ellipsize = Pango.EllipsizeMode.Middle;
		videoSpinButton.Value = 800f;
		audioSpinButton.Value = 128f;
		audioCheck.Label = "Enable audio encoding";
		audioBitrateLabel.Sensitive = false;
		audioSpinButton.Sensitive = false;
		videoBitrate = videoSpinButton.ValueAsInt;
		scrollWin.ShadowType = ShadowType.In;
		statusLabel.SetSizeRequest(120,-1);
		skipCurrentButton.Sensitive = false;
		skipCurrentButton.Label = "Skip";

		// first hbox
		image.Add(new Gtk.Image(icon));
		dropHBox.Add(image);
		dropHBox.Add(dropLabel);

		// preferences frame
		prefFrame.Add(prefVBox);
		spinVideoHBox.Add(videoBitrateLabel);
		spinVideoHBox.Add(videoSpinButton);
		spinAudioHBox.Add(audioBitrateLabel);
		spinAudioHBox.Add(audioSpinButton);
		prefVBox.Add(spinVideoHBox);
		prefVBox.Add(audioCheck);
		prefVBox.Add(spinAudioHBox);

		// status frame
		statusFrame.Add(statusVBox);
		statusVBox.Add(statusButtonHBox);
		statusVBox.Add(pBar);
		statusButtonHBox.Add(statusLabel);
		statusButtonHBox.Add(skipCurrentButton);

		// leftvbox
		leftVBox.Add(dropHBox);
		leftVBox.Add(prefFrame);
		leftVBox.Add(statusFrame);

		// right
		tree.Model = listStore;
		tree.HeadersVisible = true;
		tree.AppendColumn ("Queue", new CellRendererText (), "text", 0);

		// scrolledwindow
		scrollWin.Add(tree);

		// rightvbox
		rightVBox.Add(scrollWin);
		rightVBox.SetSizeRequest(200,-1);

		// menubar
		Menu fileMenu = new Menu ();
		MenuItem exitItem = new MenuItem("Exit");
		exitItem.Activated += new EventHandler (OnExitItemActivate);
		fileMenu.Append (exitItem);
		MenuItem fileItem = new MenuItem("File");
		fileItem.Submenu = fileMenu;
		mb.Append (fileItem);
		Menu helpMenu = new Menu ();
		MenuItem aboutItem = new MenuItem("About");
		aboutItem.Activated += new EventHandler (OnAboutItemActivate);
		helpMenu.Append (aboutItem);
		MenuItem helpItem = new MenuItem("Help");
		helpItem.Submenu = helpMenu;
		mb.Append (helpItem);

		// mainHBox
		mainVBox.Add(mb);
		mainVBox.Add(mainHBox);
		mainHBox.Add(leftVBox);
		mainHBox.Add(rightVBox);

		// window
		w.Add(mainVBox);
		w.ShowAll();

		// events
		Gtk.Drag.DestSet(dropHBox, DestDefaults.All, targetTable, Gdk.DragAction.Copy);
		dropHBox.DragDataReceived += DataReceived;
		audioCheck.Toggled += new EventHandler(OnAudioCheckToggled);
		videoSpinButton.ValueChanged += new EventHandler(GetVideoSpinButtonValue);
		audioSpinButton.ValueChanged += new EventHandler(GetAudioSpinButtonValue);
		skipCurrentButton.Clicked += new EventHandler(OnSkipOneButtonClicked);
		w.DeleteEvent += OnWindowDelete;

		Application.Run();
	}

	static void GetVideoSpinButtonValue(object source, System.EventArgs args)
	{
		SpinButton videoSpinButton = source as SpinButton;
		videoBitrate = videoSpinButton.ValueAsInt;
	}
	
	static void GetAudioSpinButtonValue(object source, System.EventArgs args)
	{
		SpinButton audioSpinButton = source as SpinButton;
		audioBitrate = audioSpinButton.ValueAsInt;
	}

	static void OnAudioCheckToggled (object obj, EventArgs args)
	{
		if (audioCheck.Active)
		{
			Gtk.Application.Invoke(delegate {
				audioBitrateLabel.Sensitive = true;
				audioSpinButton.Sensitive = true;
			});
		}
		else
		{
			Gtk.Application.Invoke(delegate {
				audioBitrateLabel.Sensitive = false;
				audioSpinButton.Sensitive = false;
			});
		}
	}

	static void OnSkipOneButtonClicked (object obj, EventArgs args)
	{
		proc.Kill();
	}
	
	static void DataReceived(object o, DragDataReceivedArgs args)
	{
		bool success = false;
		string data = System.Text.Encoding.UTF8.GetString(args.SelectionData.Data);

		switch(args.Info)
		{
			case 0:
				string [ ] uriList = Regex.Split(data, "\r\n");
				foreach(string uri in uriList)
				{
					if(uri.Length > 0)
					{
						String filename = uri.Substring(7);
						filename = System.Uri.UnescapeDataString(filename);
						Gtk.Application.Invoke(delegate {
							listStore.AppendValues(filename.Substring(filename.LastIndexOf("/")+1));
						});
						Conversion conversion = new Conversion(filename,videoBitrate);
						System.Threading.Thread tConversion = new System.Threading.Thread(conversion.DoConversion);
						tConversion.Start();
					}
				}
				success = true;
				break;
		}

		Gtk.Drag.Finish(args.Context, success, false, args.Time);
	}

	static void UpdateLabelText(Gtk.Label label, String newstring)
	{
		Gtk.Application.Invoke(delegate {
			label.Text = newstring;
		});
	}

	static bool PulseProgressbar()
	{
		if (pulsing)
		{
			Gtk.Application.Invoke(delegate {
				pBar.Text = "Converting...";
				pBar.Pulse();
			});
		}
		else
		{
			Gtk.Application.Invoke(delegate {
				pBar.Text = "";
				pBar.Fraction = 0;
			});
		}
		return pulsing;
	}

	static void OnWindowDelete(object o, DeleteEventArgs args)
	{
		Application.Quit();
	}

	static void OnExitItemActivate(object o, EventArgs args)
	{
		Application.Quit ();
	}

	static void OnAboutItemActivate(object o, EventArgs args)
	{
		AboutDialog dialog = new AboutDialog ();

		dialog.Logo = icon;
		dialog.ProgramName = "Zencoder";
		dialog.Version = "0.0.1.0";
		dialog.Comments = "A video encoding software for the Creative Zen";
		dialog.Copyright = "Copyright 2008 Andrea Cimitan";
		dialog.Website = "http://www.cimitan.com/";
		dialog.WebsiteLabel = "Visit Homepage"; 

		dialog.Run ();
		dialog.Destroy();
	}

	public class Conversion
	{
		private String filename;
		private int videoBitrate;

		public Conversion(String filename, int videoBitrate)
		{
			this.filename = filename;
			this.videoBitrate = videoBitrate;
		}

		public void DoConversion()
		{
			lock(lockI)
			{
				TreeIter iter;
				String outputfilename = filename.Substring(filename.LastIndexOf("/")+1);
				String outputpath = filename.Substring(0,filename.LastIndexOf("/"));

				pulsing = true;
				GLib.Timeout.Add (100, new GLib.TimeoutHandler (PulseProgressbar));
				UpdateLabelText(statusLabel, outputfilename);
				Gtk.Application.Invoke(delegate {
					skipCurrentButton.Sensitive = true;
				});

				proc = new System.Diagnostics.Process();
				proc.EnableRaisingEvents = false;
				proc.StartInfo.FileName = "mencoder";
				if (audioCheck.Active)
					proc.StartInfo.Arguments = " -vf scale=320:-10,expand=320:240 -oac mp3lame -lameopts abr:br=" + audioBitrate + " -ovc xvid -xvidencopts bitrate=" + videoBitrate + " '" + filename + "' -o '" + outputpath + "/" + outputfilename.Substring(0,outputfilename.LastIndexOf(".")) + " [Zen].avi'";
				else
					proc.StartInfo.Arguments = " -vf scale=320:-10,expand=320:240 -oac mp3lame -ovc xvid -xvidencopts bitrate=" + videoBitrate + " '" + filename + "' -o '" + outputpath + "/" + outputfilename.Substring(0,outputfilename.LastIndexOf(".")) + " [Zen].avi'";
				proc.Start();
				proc.WaitForExit();

				pulsing = false;
				Gtk.Application.Invoke(delegate {
					skipCurrentButton.Sensitive = false;
				});
				UpdateLabelText (statusLabel, "Idle");

				listStore.GetIterFirst (out iter);
				listStore.Remove(ref iter);
				// this will wait for the pulsing to finish
				System.Threading.Thread.Sleep (100);
			}
		}
	}
}
